home *** CD-ROM | disk | FTP | other *** search
- /**
- * ntfs_dir.c - Part of the TestDisk project.
- *
- * Copyright (c) 2004 Christophe Grenier
- *
- * Original version comes from the Linux-NTFS project.
- * Copyright (c) 2003 Lode Leroy
- * Copyright (c) 2003 Anton Altaparmakov
- * Copyright (c) 2003 Richard Russon
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program (in the main directory of the Linux-NTFS
- * distribution in the file COPYING); if not, write to the Free Software
- * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #ifdef HAVE_NTFSPROGS
- #include "config.h"
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <time.h>
- #include <string.h>
- #include <errno.h>
-
- #include "layout.h"
- #include "attrib.h"
- #define MAX_PATH 1024
- #define PATH_SEP '/'
- #define NTFS_DT_DIR 4
- #define NTFS_DT_REG 8
- #define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000)
- #include "types.h"
- #include "common.h"
- #include "intrface.h"
- #include "ntfs.h"
- #include "dir.h"
- struct list_dir_struct {
- t_file_data *dir_list;
- t_file_data *current_file;
- ntfs_volume *vol;
- };
-
- /*
- * This is the "ntfs_filldir" function type, used by ntfs_readdir() to let
- * the caller specify what kind of dirent layout it wants to have.
- * This allows the caller to read directories into their application or
- * to have different dirent layouts depending on the binary type.
- */
- typedef int (*ntfs_filldir_t)(void *dirent, const uchar_t *name,
- const int name_len, const int name_type, const s64 pos,
- const MFT_REF mref, const unsigned dt_type);
-
- extern struct ntfs_device_operations ntfs_device_testdisk_io_ops;
-
- extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
- void *dirent, ntfs_filldir_t filldir);
- extern u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
- const uchar_t *uname, const int uname_len);
- time_t ntfs2utc (s64 ntfstime);
- int list_entry( struct list_dir_struct *ls, const uchar_t *name,
- const int name_len, const int name_type, const s64 pos,
- const MFT_REF mref, const unsigned dt_type);
- ntfs_inode * utils_pathname_to_inode (ntfs_volume *vol, ntfs_inode *parent, const char *pathname);
- ATTR_RECORD * find_attribute (const ATTR_TYPES type, ntfs_attr_search_ctx *ctx);
-
- /**
- * ntfs2utc - Convert an NTFS time to Unix time
- * @time: An NTFS time in 100ns units since 1601
- *
- * NTFS stores times as the number of 100ns intervals since January 1st 1601 at
- * 00:00 UTC. This system will not suffer from Y2K problems until ~57000AD.
- *
- * Return: n A Unix time (number of seconds since 1970)
- */
- time_t ntfs2utc (s64 ntfstime)
- {
- return (ntfstime - (NTFS_TIME_OFFSET)) / 10000000;
- }
-
- /**
- * list_entry
- * FIXME: Should we print errors as we go along? (AIA)
- */
- int list_entry( struct list_dir_struct *ls, const uchar_t *name,
- const int name_len, const int name_type, const s64 pos,
- const MFT_REF mref, const unsigned dt_type)
- {
- char *filename = NULL;
- int result = 0;
-
- filename = calloc (1, MAX_PATH);
- if (!filename)
- return -1;
-
- if (ntfs_ucstombs (name, name_len, &filename, MAX_PATH) < 0) {
- ecrit_rapport("Cannot represent filename in current locale.\n");
- goto free;
- }
-
- result = 0; /* These are successful */
- if (filename[0] == '$') /* system */
- goto free;
- /* Keep FILE_NAME_WIN32 and FILE_NAME_POSIX */
- if ((name_type & FILE_NAME_WIN32_AND_DOS) == FILE_NAME_DOS)
- goto free;
- {
- s64 filesize = 0;
- ntfs_inode *ni;
- ntfs_attr_search_ctx *ctx = NULL;
- FILE_NAME_ATTR *file_name_attr;
- ATTR_RECORD *attr;
-
- result = -1; /* Everything else is bad */
-
- ni = ntfs_inode_open(ls->vol, mref);
- if (!ni)
- goto release;
-
- ctx = ntfs_attr_get_search_ctx(ni, ni->mrec);
- if (!ctx)
- goto release;
-
- if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL,
- 0, ctx))
- goto release;
- attr = ctx->attr;
-
- file_name_attr = (FILE_NAME_ATTR *)((char *)attr +
- le16_to_cpu(attr->value_offset));
- if (!file_name_attr)
- goto release;
-
-
- if (dt_type != NTFS_DT_DIR) {
- if (!ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0,
- NULL, 0, ctx))
- filesize = ntfs_get_attribute_value_length(
- ctx->attr);
- }
-
- /* if (opts.inode) */
- /* printf("%7lld %8lld %s %s\n", MREF(mref), filesize, */
- /* t_buf + 4, filename); */
- /* else */
- {
- t_file_data *new_file=MALLOC(sizeof(*new_file));
- memcpy(new_file->name,filename,(MAX_PATH<sizeof(new_file->name)?MAX_PATH:sizeof(new_file->name)));
- new_file->prev=ls->current_file;
- new_file->next=NULL;
- new_file->filestat.st_dev=0;
- new_file->filestat.st_ino=MREF(mref);
- new_file->filestat.st_mode = (dt_type == NTFS_DT_DIR?LINUX_S_IFDIR| LINUX_S_IRUGO | LINUX_S_IXUGO:LINUX_S_IFREG | LINUX_S_IRUGO);
- new_file->filestat.st_nlink=0;
- new_file->filestat.st_uid=0;
- new_file->filestat.st_gid=0;
- new_file->filestat.st_rdev=0;
- new_file->filestat.st_size=filesize;
- new_file->filestat.st_blksize=SECTOR_SIZE;
- #ifndef DJGPP
- if(new_file->filestat.st_blksize!=0)
- {
- new_file->filestat.st_blocks=(new_file->filestat.st_size+new_file->filestat.st_blksize-1)/new_file->filestat.st_blksize;
- }
- #endif
- new_file->filestat.st_atime=ntfs2utc(sle64_to_cpu(file_name_attr->last_access_time));
- new_file->filestat.st_ctime=ntfs2utc(sle64_to_cpu(file_name_attr->creation_time));
- new_file->filestat.st_mtime=ntfs2utc(sle64_to_cpu(file_name_attr->last_data_change_time));
- new_file->prev=ls->current_file;
- new_file->next=NULL;
- /* ecrit_rapport("fat: new file %s de=%p size=%u\n",new_file->name,de,de->size); */
- /* fat_write_rapport(new_fat_element); */
- if(ls->current_file!=NULL)
- ls->current_file->next=new_file;
- else
- ls->dir_list=new_file;
- ls->current_file=new_file;
- }
-
- result = 0;
- release:
- /* Release atrtibute search context and close the inode. */
- if (ctx)
- ntfs_attr_put_search_ctx(ctx);
- if (ni)
- ntfs_inode_close(ni);
- }
- free:
- free (filename);
- return result;
- }
-
- /**
- * ntfs_mount - open ntfs volume
- * @name: name of device/file to open
- * @rwflag: optional mount flags
- *
- * This function mounts an ntfs volume. @name should contain the name of the
- * device/file to mount as the ntfs volume.
- *
- * @rwflags is an optional second parameter. The same flags are used as for
- * the mount system call (man 2 mount). Currently only the following flag
- * is implemented:
- * MS_RDONLY - mount volume read-only
- *
- * The function opens the device or file @name and verifies that it contains a
- * valid bootsector. Then, it allocates an ntfs_volume structure and initializes
- * some of the values inside the structure from the information stored in the
- * bootsector. It proceeds to load the necessary system files and completes
- * setting up the structure.
- *
- * Return the allocated volume structure on success and NULL on error with
- * errno set to the error code.
- *
- * Note, that a copy is made of @name, and hence it can be discarded as
- * soon as the function returns.
- */
-
-
- /**
- * utils_pathname_to_inode - Find the inode which represents the given pathname
- * @vol: An ntfs volume obtained from ntfs_mount
- * @parent: A directory inode to begin the search (may be NULL)
- * @pathname: Pathname to be located
- *
- * Take an ASCII pathname and find the inode that represents it. The function
- * splits the path and then descends the directory tree. If @parent is NULL,
- * then the root directory '.' will be used as the base for the search.
- *
- * Return: inode Success, the pathname was valid
- * NULL Error, the pathname was invalid, or some other error occurred
- */
- ntfs_inode * utils_pathname_to_inode (ntfs_volume *vol, ntfs_inode *parent, const char *pathname)
- {
- u64 inum;
- int len;
- char *p, *q;
- ntfs_inode *ni;
- ntfs_inode *result = NULL;
- uchar_t *unicode = NULL;
- char *ascii = NULL;
-
- if (!vol || !pathname) {
- errno = EINVAL;
- return NULL;
- }
-
- if (parent) {
- ni = parent;
- } else {
- ni = ntfs_inode_open (vol, FILE_root);
- if (!ni) {
- ecrit_rapport("Couldn't open the inode of the root directory.\n");
- goto close;
- }
- }
-
- unicode = calloc (1, MAX_PATH);
- ascii = strdup (pathname); /* Work with a r/w copy */
- if (!unicode || !ascii) {
- ecrit_rapport("Out of memory.\n");
- goto close;
- }
-
- p = ascii;
- while (p && *p && *p == PATH_SEP) /* Remove leading /'s */
- p++;
- while (p && *p) {
- q = strchr (p, PATH_SEP); /* Find the end of the first token */
- if (q != NULL) {
- *q = '\0';
- q++;
- }
-
- len = ntfs_mbstoucs (p, &unicode, MAX_PATH);
- if (len < 0) {
- ecrit_rapport("Couldn't convert name to Unicode: %s.\n", p);
- goto close;
- }
-
- inum = ntfs_inode_lookup_by_name (ni, unicode, len);
- if (inum == (u64)-1) {
- ecrit_rapport("Couldn't find name '%s' in pathname '%s'.\n", p, pathname);
- goto close;
- }
-
- if (ni != parent)
- ntfs_inode_close (ni);
-
- ni = ntfs_inode_open (vol, inum);
- if (!ni) {
- ecrit_rapport("Cannot open inode %lld: %s.\n", inum, p);
- goto close;
- }
-
- p = q;
- while (p && *p && *p == PATH_SEP)
- p++;
- }
-
- result = ni;
- ni = NULL;
- close:
- if (ni && (ni != parent))
- ntfs_inode_close (ni);
- free (ascii);
- free (unicode);
- return result;
- }
-
- /**
- * find_attribute - Find an attribute of the given type
- * @type: An attribute type, e.g. AT_FILE_NAME
- * @ctx: A search context, created using ntfs_get_attr_search_ctx
- *
- * Using the search context to keep track, find the first/next occurrence of a
- * given attribute type.
- *
- * N.B. This will return a pointer into @mft. As long as the search context
- * has been created without an inode, it won't overflow the buffer.
- *
- * Return: Pointer Success, an attribute was found
- * NULL Error, no matching attributes were found
- */
- ATTR_RECORD * find_attribute (const ATTR_TYPES type, ntfs_attr_search_ctx *ctx)
- {
- if (!ctx) {
- errno = EINVAL;
- return NULL;
- }
-
- if (ntfs_attr_lookup(type, NULL, 0, 0, 0, NULL, 0, ctx) != 0) {
- ecrit_rapport("find_attribute didn't find an attribute of type: 0x%02x.\n", type);
- return NULL; /* None / no more of that type */
- }
-
- ecrit_rapport("find_attribute found an attribute of type: 0x%02x.\n", type);
- return ctx->attr;
- }
-
- t_file_data *ntfs_dir(t_param_disk *disk_car, const t_diskext *partition, t_dir_data *dir_data, const unsigned long int cluster);
- t_file_data *ntfs_dir(t_param_disk *disk_car, const t_diskext *partition, t_dir_data *dir_data, const unsigned long int cluster)
- {
- ntfs_inode *ni;
- s64 pos;
- struct list_dir_struct *ls=(struct list_dir_struct*)dir_data->private_dir_data;
- ls->dir_list=NULL;
- ls->current_file=NULL;
-
- ni = utils_pathname_to_inode (ls->vol, NULL, dir_data->current_directory);
- if (!ni) {
- /* FIXME: Print error... (AIA) */
- return NULL;
- }
-
- /*
- * We now are at the final path component. If it is a file just
- * list it. If it is a directory, list its contents.
- */
- pos = 0;
- if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
- ntfs_readdir(ni, &pos, ls, (ntfs_filldir_t)list_entry);
- /* FIXME: error checking... (AIA) */
- }
- #ifdef NEW
- else {
- ATTR_RECORD *rec;
- FILE_NAME_ATTR *attr;
- ntfs_attr_search_ctx *ctx;
- int space = 4;
- uchar_t *name = NULL;
- int name_len = 0;;
-
- ctx = ntfs_attr_get_search_ctx (NULL, ni->mrec);
- if (!ctx)
- return ls->dir_list;
-
- while ((rec = find_attribute (AT_FILE_NAME, ctx))) {
- /* We know this will always be resident. */
- attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu (rec->value_offset));
-
- if (attr->file_name_type < space) {
- name = attr->file_name;
- name_len = attr->file_name_length;
- space = attr->file_name_type;
- }
- }
-
- list_entry(ls, name, name_len, space, pos, ni->mft_no, NTFS_DT_REG);
- /* FIXME: error checking... (AIA) */
-
- ntfs_attr_put_search_ctx(ctx);
- }
- #endif
-
- /* Finished with the inode; release it. */
- ntfs_inode_close(ni);
-
- return ls->dir_list;
- }
-
- /**
- * main - Begin here
- *
- * Start from here.
- *
- * Return: 0 Success, the program worked
- * 1 Error, parsing mount options failed
- * 2 Error, mount attempt failed
- * 3 Error, failed to open root directory
- * 4 Error, failed to open directory in search path
- */
- int dir_partition_ntfs(WINDOW *window,t_param_disk *disk_car,t_diskext *partition, const int debug)
- {
- t_my_data my_data;
- static struct ntfs_device *dev;
- ntfs_volume *vol=NULL;
- aff_buffer(BUFFER_RESET,"Q");
- wmove(window,4,0);
- aff_part(window,AFF_PART_NL,disk_car,partition);
- ecrit_rapport("\n");
- aff_part_rapport(disk_car,partition);
- my_data.partition=partition;
- my_data.disk_car=disk_car;
- my_data.offset=0;
- dev = ntfs_device_alloc("/", 0, &ntfs_device_testdisk_io_ops, NULL);
- if (dev)
- {
- dev->d_private=&my_data;
- /* Call ntfs_device_mount() to do the actual mount. */
- vol = ntfs_device_mount(dev, MS_RDONLY);
- }
- if (!vol) {
- ecrit_rapport("Couldn't mount device %s\n", strerror (errno));
- return 2;
- }
-
- if (vol->flags & VOLUME_IS_DIRTY) {
- ecrit_rapport("Volume is dirty.\n");
- /* ntfs_umount (vol, FALSE); */
- /* return 2; */
- }
- {
- struct list_dir_struct ls;
- t_dir_data dir_data;
- ls.dir_list=NULL;
- ls.current_file=NULL;
- ls.vol=vol;
- dir_data.window=window;
- dir_data.debug=debug;
- dir_data.private_dir_data=&ls;
- dir_data.get_dir=ntfs_dir;
- strncpy(dir_data.current_directory,"/",sizeof(dir_data.current_directory));
- dir_partition(disk_car,partition,&dir_data,2);
- }
- ntfs_umount(vol, FALSE);
- return 0;
- }
- #else
- #include "types.h"
- #include "common.h"
- #include "intrface.h"
-
- int dir_partition_ntfs(WINDOW *window,t_param_disk *disk_car,t_diskext *partition)
- {
- aff_buffer(BUFFER_RESET,"Q");
- wmove(stdscr,5,0);
- aff_part(window,AFF_PART_NONL,disk_car,partition);
- aff_part_rapport(disk_car,partition);
- aff_buffer(BUFFER_ADD,"Recompile with ntfsprogs library");
- aff_buffer(BUFFER_DISPLAY,"Q",window);
- return 0;
- }
- #endif
-